<!doctype html>
<meta charset="utf-8" />
<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
<meta name="timeout" content="long" />
<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/invoker-utils.js"></script>

<div id="invokee"></div>
<button id="invokerbutton" commandfor="invokee" command="custom-command"></button>
<form id="aform"></form>

<script>
  aform.addEventListener('submit', (e) => (e.preventDefault()));

  function resetState() {
    invokerbutton.setAttribute("commandfor", "invokee");
    invokerbutton.setAttribute("command", "custom-command");
    invokerbutton.removeAttribute("disabled");
    invokerbutton.removeAttribute("form");
    invokerbutton.removeAttribute("type");
  }

  promise_test(async function (t) {
    let event = null;
    invokee.addEventListener("command", (e) => (event = e), { once: true });
    await clickOn(invokerbutton);
    assert_true(event instanceof CommandEvent, "event is CommandEvent");
    assert_equals(event.type, "command", "type");
    assert_equals(event.bubbles, false, "bubbles");
    assert_equals(event.composed, true, "composed");
    assert_equals(event.isTrusted, true, "isTrusted");
    assert_equals(event.command, "custom-command", "command");
    assert_equals(event.target, invokee, "target");
    assert_equals(event.invoker, invokerbutton, "invoker");
  }, "event dispatches on click");

  // valid custom invokeactions
  ["-foo", "foo-", "cAsE-cArRiEs", "-", "-a-", "a-b", "---", "show-picker"].forEach(
    (command) => {
      promise_test(async function (t) {
        t.add_cleanup(resetState);
        let event = null;
        invokee.addEventListener("command", (e) => (event = e), { once: true });
        invokerbutton.command = command;
        await clickOn(invokerbutton);
        assert_true(event instanceof CommandEvent, "event is CommandEvent");
        assert_equals(event.type, "command", "type");
        assert_equals(event.bubbles, false, "bubbles");
        assert_equals(event.composed, true, "composed");
        assert_equals(event.isTrusted, true, "isTrusted");
        assert_equals(event.command, command, "command");
        assert_equals(event.target, invokee, "target");
        assert_equals(event.invoker, invokerbutton, "invoker");
      }, `setting custom command property to ${command} (must include dash) sets event command`);

      promise_test(async function (t) {
        t.add_cleanup(resetState);
        let event = null;
        invokee.addEventListener("command", (e) => (event = e), { once: true });
        invokerbutton.setAttribute("command", command);
        await clickOn(invokerbutton);
        assert_true(event instanceof CommandEvent, "event is CommandEvent");
        assert_equals(event.type, "command", "type");
        assert_equals(event.bubbles, false, "bubbles");
        assert_equals(event.composed, true, "composed");
        assert_equals(event.isTrusted, true, "isTrusted");
        assert_equals(event.command, command, "command");
        assert_equals(event.target, invokee, "target");
        assert_equals(event.invoker, invokerbutton, "invoker");
      }, `setting custom command attribute to ${command} (must include dash) sets event command`);
    },
  );

  // invalid custom invokeactions
  ["foo", "foobar", "foo bar", "em—dash", "hidedocument"].forEach((command) => {
    promise_test(async function (t) {
      t.add_cleanup(resetState);
      let event = null;
      invokee.addEventListener("command", (e) => (event = e), { once: true });
      invokerbutton.command = command;
      await clickOn(invokerbutton);
      assert_equals(event, null, "event should not have fired");
    }, `setting custom command property to ${command} (no dash) did not dispatch an event`);

    promise_test(async function (t) {
      t.add_cleanup(resetState);
      let event = null;
      invokee.addEventListener("command", (e) => (event = e), { once: true });
      invokerbutton.setAttribute("command", command);
      await clickOn(invokerbutton);
      assert_equals(event, null, "event should not have fired");
    }, `setting custom command attribute to ${command} (no dash) did not dispatch an event`);
  });

  promise_test(async function (t) {
    let called = false;
    invokerbutton.addEventListener(
      "click",
      (event) => {
        event.preventDefault();
      },
      { once: true },
    );
    invokee.addEventListener(
      "command",
      (event) => {
        called = true;
      },
      { once: true },
    );
    await clickOn(invokerbutton);
    assert_false(called, "event was not called");
  }, "event does not dispatch if click:preventDefault is called");

  promise_test(async function (t) {
    t.add_cleanup(resetState);
    let called = false;
    invokee.addEventListener("command", (e) => (called = true), { once: true });
    invokerbutton.setAttribute("disabled", "");
    await clickOn(invokerbutton);
    assert_false(called, "event was not called");
  }, "event does not dispatch if invoker is disabled");

  promise_test(async function (t) {
    t.add_cleanup(resetState);
    let called = false;
    invokee.addEventListener("command", (e) => (called = true), { once: true });
    invokerbutton.setAttribute("form", "aform");
    await clickOn(invokerbutton);
    assert_false(called, "event was not called");
  }, "event does not dispatch if invoker is form associated without `type`");

  promise_test(async function (t) {
    t.add_cleanup(resetState);
    let called = false;
    invokee.addEventListener("command", (e) => (called = true), { once: true });
    invokerbutton.setAttribute("form", "aform");
    invokerbutton.setAttribute("type", "button");
    await clickOn(invokerbutton);
    assert_true(called, "event was not called");
  }, "event dispatches if invoker is form associated with `type=button`");

  promise_test(async function (t) {
    svgInvokee = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgInvokee.setAttribute("id", "svg-invokee");
    t.add_cleanup(resetState);
    document.body.append(svgInvokee);
    assert_false(svgInvokee instanceof HTMLElement);
    assert_true(svgInvokee instanceof Element);
    let event = null;
    svgInvokee.addEventListener("command", (e) => (event = e), { once: true });
    invokerbutton.setAttribute("commandfor", "svg-invokee");
    invokerbutton.setAttribute("command", "custom-command");
    assert_equals(invokerbutton.commandForElement, svgInvokee);
    await clickOn(invokerbutton);
    assert_not_equals(event, null, "event was called");
    assert_true(event instanceof CommandEvent, "event is CommandEvent");
    assert_equals(event.invoker, invokerbutton, "event.invoker is set to right element");
    assert_equals(event.target, svgInvokee, "event.target is set to right element");
  }, "event dispatches if invokee is non-HTML Element");
</script>
